`include "../../entrega_b/codigo/Comp.v"
`include "../../entrega_d/codigo/Controle.v"
`include "../../entrega_b/codigo/Shifter.v"

module Decode (

    input               clock,
    input               reset,
    input               fw_if_id_stall,

    //Fetch
    input       [31:0]  if_id_instrucao,
    input       [31:0]  if_id_proximopc,
    output reg          id_if_selfontepc,
    output      [31:0]  id_if_rega,
    output reg  [31:0]  id_if_pcimd2ext,
    output reg  [31:0]  id_if_pcindex,
    output reg   [1:0]  id_if_seltipopc,

    //Execute
    output reg          id_ex_selalushift,
    output reg          id_ex_selimregb,
    output reg          id_ex_selsarega,
    output reg   [2:0]  id_ex_aluop,
    output reg          id_ex_unsig,
    output reg   [1:0]  id_ex_shiftop,
    output reg   [4:0]  id_ex_shiftamt,
    output      [31:0]  id_ex_rega,
    output reg   [2:0]  id_ex_msm,
    output reg   [2:0]  id_ex_msl,
    output reg          id_ex_readmem,
    output reg          id_ex_writemem,
    output reg          id_ex_mshw,
    output reg          id_ex_lshw,
    output      [31:0]  id_ex_regb,
    output reg  [31:0]  id_ex_imedext,
    output reg  [31:0]  id_ex_proximopc,
    output reg   [2:0]  id_ex_selwsource,
    output reg   [4:0]  id_ex_regdest,
    output reg          id_ex_writereg,
    output reg          id_ex_writeov,

    //Forwarding 
    output       [4:0]  id_fw_regdest,
    output              id_fw_load,
    output       [4:0]  id_fw_addra,
    output       [4:0]  id_fw_addrb,
    output      [31:0]  id_fw_rega,
    output      [31:0]  id_fw_regb,
    input       [31:0]  fw_id_rega,
    input       [31:0]  fw_id_regb,
    

    //Registradores
    output       [4:0]  id_reg_addra,
    output       [4:0]  id_reg_addrb,
    input       [31:0]  reg_id_dataa,
    input       [31:0]  reg_id_datab,
    output              id_reg_ena,
    output              id_reg_enb

    );

    // ********** Instanciacao do controle ********** //
    // Port wires para o controle.
    wire   [5:0]    op ;
    wire   [5:0]    fn ;
    wire   [4:0]    rt ;
    wire   [2:0]    selwsource ;
    wire   [1:0]    selregdest ;
    wire            writereg ;
    wire            writeov ;
    wire            selimregb ;
    wire            selsarega ;
    wire            selalushift ;
    wire   [2:0]    aluop ;
    wire            unsig ;
    wire   [1:0]    shiftop ;
    wire            mshw ;
    wire            lshw ;
    wire   [2:0]    msm ;
    wire   [2:0]    msl ;
    wire            readmem ;
    wire            writemem ;
    wire   [1:0]    selbrjumpz ;
    wire   [1:0]    seltipopc ;
    wire   [2:0]    compop;

    // Instanciacao do controle.
    Controle controle(op,fn,rt,selwsource,selregdest,writereg,writeov,selimregb,selsarega,selalushift,aluop,unsig,shiftop,mshw,lshw,msm,msl,readmem,writemem,selbrjumpz,seltipopc,compop);

    // ********** Instanciacao do comparador ********** //
    // Port wires para o comparador.
    wire compout; 

    // Instanciacao do comparador. Lembre-se que a operacao vem do
    // controle (compop).
    Comp comp(comp_rega,comp_regb,compop,compout);

    // ********** Instanciacao do shifter(immediate) ********** //
    wire [31:0] w_im_extend;
    wire [31:0] w_im_extend_shift;

    Shifter shift_immediate(w_im_extend,2'b10,2'b10,w_im_extend_shift);

    // ********** Instanciacao do shifter(index) ********** //
    wire [31:0] w_index_extend;
    wire [31:0] w_index_extend_shift;

    Shifter shift_index(w_index_extend,2'b10,2'b10,w_index_extend_shift);


    // ********* Declaracoes ********** //
    reg [31:0] comp_rega;
    reg [31:0] comp_regb;
    reg [31:0] im_extend;
    reg [31:0] im_extend_shift;
    reg [31:0] index_extend;
    reg [31:0] index_extend_shift;
    reg [31:0] reg_id_ex_rega;
    reg [31:0] reg_id_ex_regb;
    reg [4:0]  reg_id_reg_addra;
    reg [4:0]  reg_id_reg_addrb;
    reg        reg_id_reg_ena;
    reg        reg_id_reg_enb;
    reg [4:0]  op_aux;
    reg [4:0]  reg_id_fw_regdest;
    reg        reg_id_fw_load;
    reg [4:0]  reg_id_fw_addra;
    reg [4:0]  reg_id_fw_addrb;
    reg [31:0] reg_id_fw_rega;
    reg [31:0] reg_id_fw_regb;
    reg [31:0] reg_id_if_rega;
 
    // ********** Assigns ********** //
    // Repassa os pedacos da instrucao para o controle. 
    assign op = if_id_instrucao[31:26];
    assign rt = if_id_instrucao[20:16];
    assign fn = if_id_instrucao[5:0];
     
    // Repassa o valor para leitura do registrador A.
    assign id_reg_addra = if_id_instrucao[25:21]; 
    
    // Repassa o valor estendido para o shifter.
    assign w_im_extend = im_extend;
    
    // Repassa o valor estendido para o shifter.
    assign w_index_extend = index_extend;
    
    // Repassa os valores de a e b para a saída.
    assign id_ex_rega = reg_id_ex_rega;
    assign id_ex_regb = reg_id_ex_regb;  
    
    // Repassa os valores para o modulo de registradores.
    assign id_reg_addra = reg_id_reg_addra;
    assign id_reg_addrb = reg_id_reg_addrb;
    assign id_reg_ena = reg_id_reg_ena;
    assign id_reg_enb = reg_id_reg_enb;

    // Repassa os valores de forward.
    assign id_fw_regdest = reg_id_fw_regdest;
    assign id_fw_load = reg_id_fw_load;
    assign id_fw_addra = reg_id_fw_addra;
    assign id_fw_addrb = reg_id_fw_addrb;
    assign id_fw_rega = reg_id_fw_rega;
    assign id_fw_regb = reg_id_fw_regb;

    // Repassa o valor do endereço A para a saída para o Fetch.
    assign id_if_rega = reg_id_if_rega;

    // ********** Inicio ********** //
    // Trata o caso reset, repassando 0 para o EX.
    always @(posedge(reset)) begin
        id_ex_selalushift = 1'b0;
        id_ex_selimregb = 1'b0;
        id_ex_selsarega = 1'b0;
        id_ex_aluop = 3'b000;
        id_ex_unsig = 1'b0;     
        id_ex_shiftop = 2'b00;
        id_ex_shiftamt = 5'b00000;
        reg_id_ex_rega = 32'b00000000000000000000000000000000;
        id_ex_msm = 3'b000;
        id_ex_msl = 3'b000; 
        id_ex_readmem = 1'b0; 
        id_ex_writemem = 1'b0;
        id_ex_mshw = 1'b0;     
        id_ex_lshw = 1'b0;     
        reg_id_ex_regb = 32'b00000000000000000000000000000000;
        id_ex_imedext = 32'b00000000000000000000000000000000;
        id_ex_proximopc = 32'b00000000000000000000000000000000;
        id_ex_selwsource = 3'b000;
        id_ex_regdest = 5'b00000;
        id_ex_writereg = 1'b0;
        id_ex_writeov = 1'b0;  
    end

    always @(negedge(clock)) begin
        
        // Trata o caso de Stall
        if( !fw_if_id_stall ) begin

            // Repassa o proximo PC.
            id_ex_proximopc = if_id_proximopc;

            // ########################## //
            // ########## Fetch ######### //
            //########################### //

            // Estende o sinal do imediato e repassa ao shifter.
            im_extend[15:0] = if_id_instrucao[15:0];
	        if(im_extend[15] == 1'b1) begin
                im_extend[31:16] = 16'b1111111111111111;
            end else begin
                im_extend[31:16] = 16'b0000000000000000;
            end
            // Pega a saída.
            im_extend_shift = w_im_extend_shift; 
              
            // Vai dar pau pois não faz sentido algum o proximopc 
            // na especificacao.
            id_if_pcimd2ext = im_extend_shift + if_id_proximopc;



            id_if_seltipopc = seltipopc; 

            // Estende o sinal do index e repassa ao shifter.
            index_extend[25:0] = if_id_instrucao[25:0];
            index_extend[31:26] = 6'b000000;
       
            // Pega a saída.
            index_extend_shift = w_index_extend_shift;
 
            // Manipula os fios para fazer id_if_pcindex.
            id_if_pcindex[31:28] = if_id_proximopc[31:28];
            id_if_pcindex[27:0] = index_extend_shift[27:0];

            // #################################### //
            // ########## Registradores ########### //
            // #################################### //

            reg_id_reg_ena = 1'b1;
            reg_id_reg_enb = 1'b1;           
            reg_id_reg_addra = if_id_instrucao[25:21];

            op_aux = if_id_instrucao[31:26];
       
            if(op_aux == 5'd1 || op_aux == 5'd6 || op_aux == 5'd7) begin
                reg_id_reg_addrb = 5'b00000;
            end else begin
                reg_id_reg_addrb = if_id_instrucao[20:16];
            end 
	
            // ############################## //
            // ########## Forward ########### //
            // ############################## //

            reg_id_fw_load = readmem;
            reg_id_fw_addra = if_id_instrucao[25:21];
            reg_id_fw_addrb = if_id_instrucao[20:16];
            reg_id_fw_rega = reg_id_dataa;
            reg_id_fw_regb = reg_id_datab;
           
            // ############################## //
            // ########## Execute ########### //
            // ############################## //

            id_ex_selalushift = selalushift;
            id_ex_selimregb = selimregb;
            id_ex_selsarega = selsarega;
            id_ex_aluop = aluop;
            id_ex_unsig = unsig;     
            id_ex_shiftop = shiftop;
            id_ex_shiftamt = if_id_instrucao[10:6];
            reg_id_ex_rega = fw_id_rega; 
            id_ex_msm = msm;
            id_ex_msl = msl; 
            id_ex_readmem = readmem; 
            id_ex_writemem = writemem;
            id_ex_mshw = mshw;     
            id_ex_lshw = lshw;     
            reg_id_ex_regb = fw_id_regb; 
            id_ex_imedext[15:0] = if_id_instrucao[31:16];
       
            if(id_ex_imedext[15] == 1'b1) begin
                id_ex_imedext[31:16] = 16'b1111111111111111;
            end else begin
		        id_ex_imedext[31:16] = 16'b0000000000000000;
            end
       
            id_ex_selwsource = selwsource;
            
            // Seleciona o registrador de escrita.
            if (selregdest == 0) begin
                id_ex_regdest = if_id_instrucao[20:16];
                reg_id_fw_regdest = if_id_instrucao[20:16];
            end
       
            if (selregdest == 1) begin
                id_ex_regdest = if_id_instrucao[15:11];
                reg_id_fw_regdest = if_id_instrucao[15:11];
            end 
       
            if (selregdest == 0) begin
                id_ex_regdest = 5'd31;
                reg_id_fw_regdest = 5'd31;
            end
       
            if (selregdest == 1) begin
                id_ex_regdest = 5'dx;
                reg_id_fw_regdest = 5'dx;
            end           
       
            id_ex_writereg = writereg;
            id_ex_writeov = writeov;             

            // ################################ //
            // ########## Pendencias ########## //
            // ################################ //

            // Retorna o valor do endereço A (rs) para o Fetch.
            reg_id_if_rega = reg_id_dataa;

            // Repasse para o comparador.
            comp_rega = reg_id_dataa;
            comp_regb = reg_id_datab;

            // Seletor de fonte do pc.
            if(selbrjumpz == 0) begin
                id_if_selfontepc = 1'b0;
            end
            if(selbrjumpz == 1) begin
                id_if_selfontepc = 1'b1;
            end
            if(selbrjumpz == 2) begin
                id_if_selfontepc = compout;
            end
            if(selbrjumpz == 3) begin
                id_if_selfontepc = 1'b0;
            end

        end else begin   

            id_ex_selalushift = 1'b0;
            id_ex_selimregb = 1'b0;
            id_ex_selsarega = 1'b0;
            id_ex_aluop = 3'b000;
            id_ex_unsig = 1'b0;     
            id_ex_shiftop = 2'b00;
            id_ex_shiftamt = 5'b00000;
            reg_id_ex_rega = 32'b00000000000000000000000000000000;
            id_ex_msm = 3'b000;
            id_ex_msl = 3'b000; 
            id_ex_readmem = 1'b0; 
            id_ex_writemem = 1'b0;
            id_ex_mshw = 1'b0;     
            id_ex_lshw = 1'b0;     
            reg_id_ex_regb = 32'b00000000000000000000000000000000;
            id_ex_imedext = 32'b00000000000000000000000000000000;
            id_ex_proximopc = 32'b00000000000000000000000000000000;
            id_ex_selwsource = 3'b000;
            id_ex_regdest = 5'b00000;
            id_ex_writereg = 1'b0;
            id_ex_writeov = 1'b0;  
        end 
    end 

endmodule
